package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"
)

type ElectrsRequest struct {
	Method string        `json:"method"`
	Params []interface{} `json:"params"`
	ID     int           `json:"id"`
}
type ElectrsPool struct {
	address    string
	maxConns   int
	connChan   chan net.Conn
	mu         sync.Mutex
	activeConn int
}

// 初始化连接池
func NewElectrsPool(electrsAddr string, maxConns int) *ElectrsPool {
	return &ElectrsPool{
		address:  electrsAddr,
		maxConns: maxConns,
		connChan: make(chan net.Conn, maxConns),
	}
}

// 获取连接
func (p *ElectrsPool) getConn() (net.Conn, error) {
	select {
	case conn := <-p.connChan:
		if isConnClosed(conn) {
			p.decreaseConn()
			return p.dialNewConn()
		}
		return conn, nil
	default:
		p.mu.Lock()
		if p.activeConn < p.maxConns {
			p.activeConn++
			p.mu.Unlock()
			return p.dialNewConn()
		}
		p.mu.Unlock()

		// 等待一个空闲连接（阻塞）
		conn := <-p.connChan
		if isConnClosed(conn) {
			p.decreaseConn()
			return p.dialNewConn()
		}
		return conn, nil
	}
}

func (p *ElectrsPool) dialNewConn() (net.Conn, error) {
	conn, err := net.DialTimeout("tcp", p.address, 5*time.Second)
	if err != nil {
		p.decreaseConn()
		return nil, err
	}
	return conn, nil
}

func (p *ElectrsPool) returnConn(conn net.Conn) {
	if conn == nil || isConnClosed(conn) {
		if conn != nil {
			conn.Close()
		}
		p.decreaseConn()
		return
	}
	select {
	case p.connChan <- conn:
	default:
		conn.Close()
		p.decreaseConn()
	}
}

func (p *ElectrsPool) decreaseConn() {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.activeConn > 0 {
		p.activeConn--
	}
}

func (p *ElectrsPool) Call(method string, params []interface{}) (string, error) {
	conn, err := p.getConn()
	if err != nil {
		return "", err
	}
	defer func() {
		p.returnConn(conn)
	}()

	_ = conn.SetDeadline(time.Now().Add(15 * time.Second))

	req := ElectrsRequest{
		Method: method,
		Params: params,
		ID:     0,
	}

	data, err := json.Marshal(req)
	if err != nil {
		return "", err
	}

	_, err = fmt.Fprintf(conn, "%s\n", data)
	if err != nil {
		conn.Close()
		p.decreaseConn()
		return "", err
	}

	resp, err := bufio.NewReader(conn).ReadBytes('\n')
	if err != nil {
		conn.Close()
		p.decreaseConn()
		return "", err
	}

	return string(resp), nil
}

// 判断连接是否关闭
func isConnClosed(conn net.Conn) bool {
	if conn == nil {
		return true
	}
	one := make([]byte, 1)
	conn.SetReadDeadline(time.Now().Add(1 * time.Millisecond))
	_, err := conn.Read(one)
	if err != nil {
		if strings.Contains(err.Error(), "timeout") || strings.Contains(err.Error(), "i/o timeout") {
			return false
		}
		return true
	}
	return false // 正常读到内容，认为连接有效
}
